/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab-modules.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_TetraComplex_ModelStructure_h__
#define __dylab_TetraComplex_ModelStructure_h__

//===========================================================================================
#include <dylab/utils/topology/TetraComplex.h>
#include <dylab/simobject/masspoints/MassPointsFeatureBase.h>
#include <dylab/simobject/tetravol/TetrahedralVolumeFeatureBase.h>
#include <dylab/simobject/boundarymesh/BoundaryMeshFeatureBase.h>

//===========================================================================================
namespace dylab {

    /**
     */
    class TetraComplex_ModelStructure 
        : public ComModelStructure
        , public MassPointsFeatureBase
        , public TetrahedralVolumeFeatureBase
        , public BoundaryMeshFeatureBase
        , public TetraComplex<>
        , public SimplicalComplex::ISimplexListener
    {
    public:
        ///
        static const String COMPONENT_NAME;
    private:
        //
        typedef TetraComplex<>::NodeSimplex  NodeSimplex;
        typedef TetraComplex<>::EdgeSimplex  EdgeSimplex;
        typedef TetraComplex<>::TriSimplex   TriSimplex;
        typedef TetraComplex<>::TetraSimplex TetraSimplex;
    private:
        //-----------------------------------------------------------------------------
        /// custom mass point
        class _MassPoint : public MassPoint
        {
        private:
            ///
            NodeSimplex * mNodeSimplex;
        public:
            ///
            _MassPoint(NodeSimplex * _node)
                : MassPoint(_node->getId()), mNodeSimplex(_node)
                    { DYLAB_ASSERT(mNodeSimplex != NULL); }
            ///
            NodeSimplex * getNodeSimplex()
                    { return mNodeSimplex; }
        };
        //-----------------------------------------------------------------------------
        /// custom edge
        class _Edge : public Edge
        {
        private:
            ///
            EdgeSimplex * mEdgeSimplex;
        public:
            ///
            _Edge(EdgeSimplex * _complexEdge, IMassPointsFeature * _massPoints)
                : mEdgeSimplex(_complexEdge)
                    { 
                        // check the pointers
                        DYLAB_ASSERT(mEdgeSimplex != NULL); 
                        DYLAB_ASSERT(_massPoints != NULL);

                        // store edge id
                        mId = mEdgeSimplex->getId();

                        // store node ids as mass points ids
                        for (EdgeGeometry3::VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++)
                            mMassPoints[vertexIdx] = _massPoints->massPointReader().get(mEdgeSimplex->getNodeID(vertexIdx));
                    }
            ///
            EdgeSimplex * getEdgeSimplex()
                    { return mEdgeSimplex; }
        };
        //-----------------------------------------------------------------------------
        /// custom triangle
        class _Triangle : public Triangle
        {
        private:
            ///
            TriSimplex * mTriSimplex;
        public:
            ///
            _Triangle(TriSimplex * _tri, IMassPointsFeature * _massPoints)
                : mTriSimplex(_tri)
                    { 
                        // check the pointers
                        DYLAB_ASSERT(mTriSimplex != NULL); 
                        DYLAB_ASSERT(_massPoints != NULL);

                        // store triangle id
                        mId = mTriSimplex->getId();

                        // store node ids as mass points ids                        
                        for (TriangleGeometry3::VertexIdxIterator vertexIdx; vertexIdx.isValid(); vertexIdx++)
                            mMassPoints[vertexIdx] = _massPoints->massPointReader().get(mTriSimplex->getNodeID(vertexIdx));

                        // the boundary flag is set in the generateFromTetrahedralVolume() method (when generating boundary mesh) 
                        mIsBoundary = false;
                    }
            ///
            virtual Edge::id_t getEdgeID(TriangleGeometry3::edgeidx_t _edgeIdx) const
                    { 
                        DYLAB_ASSERT(mTriSimplex != NULL); 
                        return mTriSimplex->getEdgeID(_edgeIdx);
                    }
            ///
            TriSimplex * getTriSimplex()
                    { return mTriSimplex; }
        };
        //-----------------------------------------------------------------------------
        /// custom tetrahedron
        class _Tetrahedron : public Tetrahedron
        {
        private:
            ///
            TetraSimplex * mTetraSimplex;
        public:
            ///
            _Tetrahedron(TetraSimplex * _tetra, MassPoint * _mp1, MassPoint * _mp2, MassPoint * _mp3, MassPoint * _mp4)
				: Tetrahedron(_tetra != NULL ? _tetra->getId() : 0, _mp1, _mp2, _mp3, _mp4), mTetraSimplex(_tetra) 
                    { DYLAB_ASSERT(mTetraSimplex != NULL); }
            ///
            _Tetrahedron(TetraSimplex * _tetra, MassPoint * (&_mp)[TetrahedronGeometry::V_COUNT])
                : Tetrahedron(_tetra != NULL ? _tetra->getId() : 0, _mp), mTetraSimplex(_tetra) 
                    { DYLAB_ASSERT(mTetraSimplex != NULL); }
            ///
            virtual Edge::id_t getEdgeID(TetrahedronGeometry::edgeidx_t _edgeIdx) const
                    { 
                        DYLAB_ASSERT(mTetraSimplex != NULL); 
                        return mTetraSimplex->getEdgeID(_edgeIdx);
                    }
            ///
            virtual Triangle::id_t getTriangleID(TetrahedronGeometry::faceidx_t _faceIdx) const
                    {  
                        DYLAB_ASSERT(mTetraSimplex != NULL); 
                        return mTetraSimplex->getTriID(_faceIdx);
                    }
            ///
            TetraSimplex * getTetraSimplex()
                    { return mTetraSimplex; }
        };
        //-----------------------------------------------------------------------------
        /// custom boundary vertex
        class _Vertex : public Vertex
        {
        private:
            ///
            MassPoint * mMassPoint;
        public:
            ///
            _Vertex(MassPoint * _mp, uint32_t _index, const Vector3 & _position, const Vector3 & _normal, real_t _u, real_t _v)
                : Vertex(_index, _position, _normal, _u, _v), mMassPoint(_mp)
                    { }
            ///
            MassPoint * getMassPoint()
                    { return mMassPoint; }
            /// updates the position, if needed, and if so, returns true, otherwise returns false
            bool updatePosition()
                    { 
                        DYLAB_ASSERT(mMassPoint != NULL);

                        // position has changed
                        if (mPosition != mMassPoint->currState().position)
                        {
                            // update the position
                            mPosition = mMassPoint->currState().position;
                            return true;
                        }

                        // position was not updated
                        return false;
                    }
        };
        //-----------------------------------------------------------------------------
    private:
        //
        typedef MassPointContainer   NodeDataContainer;
        typedef EdgeContainer        EdgeDataContainer;
        typedef TriangleContainer    TriDataContainer;
        typedef TetrahedronContainer TetraDataContainer;
        //
        typedef HashContainer<MassPoint::id_t, uint32_t>    NodeVertexMap;
        typedef VertexContainer                           BoundaryVertexContainer;
        typedef FaceContainer                             BoundaryFaceContainer;
    private:
		// pointer to the parent simulation object 
		SimObject * mParentSimObject;
        // the first available node id, so that no existing node id has greater value
        uint32_t mCurrNodeIDCap;
        //        
        NodeDataContainer   mNodeDataContainer;
        EdgeDataContainer   mEdgeDataContainer;
        TriDataContainer    mTriDataContainer;
        TetraDataContainer  mTetraDataContainer;
        //
        NodeVertexMap           mNodeVertexMap;
        BoundaryVertexContainer mBoundaryVertexContainer;
        BoundaryFaceContainer   mBoundaryFaceContainer;
        bool                    mBoundaryMeshGeometryNeedsUpdate;
        bool                    mBoundaryMeshTopologyNeedsUpdate;

    public:        

        //-----------------------------------------------------------------------------
        // Constructors & destructor
        //-----------------------------------------------------------------------------        

        TetraComplex_ModelStructure();                
        virtual ~TetraComplex_ModelStructure();

        //-----------------------------------------------------------------------------
        // ISimplexListener methods
        //-----------------------------------------------------------------------------        

        virtual void onSimplexAdded(Simplex * simplex);        
        virtual void onSimplexRemoved(Simplex * simplex);        
        virtual ISimplexListener * clone() const;

        //-----------------------------------------------------------------------------
        // ISimObjectComponent methods
        //-----------------------------------------------------------------------------

		virtual String getDescription() const;
		virtual void initialize(SimObject * _parentSimObject);
        virtual SimObject * getParentSimObject()
				{ return mParentSimObject; }
        virtual const SimObject * getParentSimObject() const
				{ return mParentSimObject; }

        //-----------------------------------------------------------------------------
        // ComModelStructure methods
        //-----------------------------------------------------------------------------

        virtual void translate(const Vector3 & _trans);
        virtual void rotate(const Vector3 & _axis, real_t _radians);
        virtual void scale(const Vector3 & _scale);

        //-----------------------------------------------------------------------------
        // MassPointsFeatureBase methods
        //-----------------------------------------------------------------------------        
        
        virtual MassPoint::id_t createMassPoints(uint32_t count);
        virtual MassPointContainer::Iterator massPointIterator();
        virtual MassPointContainer::ConstIterator massPointIterator() const;
        virtual MassPointContainer::Reader massPointReader() const;
        virtual void prepareForPointRendering();
        virtual MassPoint::id_t getNearestMassPointId(const Vector3 & _pos);


        //-----------------------------------------------------------------------------
        // TetrahedralVolumeFeatureBase methods
        //-----------------------------------------------------------------------------        
        
		virtual Tetrahedron::id_t	createTetrahedron(MassPoint::id_t _mp1, MassPoint::id_t _mp2, 
			MassPoint::id_t _mp3, MassPoint::id_t _mp4);
		virtual void				destroyTetrahedron(Tetrahedron::id_t _tetraID);

        virtual void subdivideTetrahedron(Tetrahedron::id_t _tetraID, const Vector3 & _point);
        virtual void subdivideEdge(Edge::id_t _edgeID, const Vector3 & _point);
        virtual void subdivideTriangle(Triangle::id_t _triID, const Vector3 & _point);
        virtual void collapseEdge(Edge::id_t _edgeID);        

        virtual EdgeContainer::Iterator			edgeIterator();
        virtual EdgeContainer::ConstIterator	edgeIterator() const;
        virtual EdgeContainer::Reader			edgeReader() const;

        virtual TriangleContainer::Iterator			triangleIterator();
        virtual TriangleContainer::ConstIterator	triangleIterator() const;        
        virtual TriangleContainer::Reader			triangleReader() const;

        virtual TetrahedronContainer::Iterator		tetrahedronIterator();
        virtual TetrahedronContainer::ConstIterator tetrahedronIterator() const;
        virtual TetrahedronContainer::Reader		tetrahedronReader() const;

        virtual void prepareForVolumeRendering();

        //-----------------------------------------------------------------------------
        // BoundaryMeshFeatureBase methods
        //-----------------------------------------------------------------------------        
        
        virtual void generateFromMassPoints();
        virtual void generateFromTetrahedralVolume();

        virtual uint32_t	createVertex(const Vector3 & _position, const Vector3 & _normal, real_t _u, real_t _v);
		virtual Face::id_t	createFace(uint32_t  _v0, uint32_t  _v1, uint32_t _v2);

        virtual VertexContainer::Iterator		vertexIterator();
        virtual VertexContainer::ConstIterator	vertexIterator() const;
        virtual VertexContainer::Reader			vertexReader() const;

        virtual FaceContainer::Iterator			faceIterator();
        virtual FaceContainer::ConstIterator	faceIterator() const;
        virtual FaceContainer::Reader			faceReader() const;

        virtual void prepareForRendering();
    };
}

//===========================================================================================
#endif // __dylab_TetraComplex_ModelStructure_h__